/*
 * DensityPlot.java
 */

package complexchaos;

/**
 * This class is designed to calculate a density plot based on a complex valued
 * function and a complex seed in a region of the complex plane. It takes a
 * region in the complex plane, divides it into a bunch of subsections. It then
 * iterates the function F(z) on the seed z0 N times, where N is the maximum
 * number of iterations passed to the constructor. At each iteration, it
 * determines which box in the region the point is located. It then adds one to
 * a place in a matrix corresponding to that box in the grid. After iterating N
 * times, the matrix will contain the number of times the orbit of z0 landed in
 * each square of the grid. This data can be used to generate either a 3D
 * histogram or a density plot. These can be used to determine whether a certain
 * function is chaotic in a region given a specific seed.
 *
 * @author Dan Kotowski
 */
public class DensityPlot {
    private ExpFormula F, ExpSeed;
    private ComplexNumber ComplexSeed, lowerBound, upperBound;
    private int gridDivisions, N;
    private int[][] densityCount;
    private int max;

    /**
     * Class constructor.
     * @param histLowerBound lower left complex bound of the region
     * @param histUpperBound upper right complex bound of the region
     * @param complexF complex valued function to iterate on
     * @param histIterations maximum number of iterations
     * @param complexSeed seed to use
     * @param histGridDivisions number of grid divisions on the axis
     */
    public DensityPlot (ComplexNumber histLowerBound, ComplexNumber histUpperBound, ExpFormula complexF, int histIterations, ComplexNumber complexSeed, int histGridDivisions) {
        F = complexF;
        ComplexSeed = complexSeed.clone();
        ExpSeed = new ExpFormula(complexSeed.toString());
        lowerBound = histLowerBound.clone();
        upperBound = histUpperBound.clone();
        gridDivisions = histGridDivisions;
        N = histIterations;
        densityCount = new int[gridDivisions][gridDivisions];
        for (int i = 0; i < gridDivisions; i++)
            for (int j = 0; j < gridDivisions; j++)
                densityCount[i][j] = 0;
        max = 0;
        densityCount();
    }

    /**
     * Computes the matrix that holds the number of times the orbit of z0 lands
     * in each box on the grid.
     */
    public void densityCount() {
        VarValList ExpVVList = new VarValList("z", ExpSeed);
        F = F.evaluate(ExpVVList.getList());
        ComplexNumber gridZ = new ComplexNumber( (ComplexSeed.getReal().approx() - lowerBound.getReal().approx()) * gridDivisions / Math.abs( upperBound.getReal().approx() - lowerBound.getReal().approx() ), (ComplexSeed.getImag().approx() - lowerBound.getImag().approx()) * gridDivisions / Math.abs( upperBound.getReal().approx() - lowerBound.getReal().approx() ));
        if (gridZ.getReal().approx() >= 0 && gridZ.getImag().approx() >= 0 && gridZ.getReal().approx() < gridDivisions && gridZ.getImag().approx() < gridDivisions) {
            int x = (int)Math.floor(gridZ.getReal().approx());
            int y = (int)Math.floor(gridZ.getImag().approx());
            densityCount[x][y]++;
            if(densityCount[x][y] > max) max = densityCount[x][y];
        }
        for (int i = 0; i < N; i++) {
            ComplexNumber tempZ = new ComplexNumber( (F.getNumber().getReal().approx() - lowerBound.getReal().approx()) * gridDivisions / Math.abs( upperBound.getReal().approx() - lowerBound.getReal().approx() ), (F.getNumber().getReal().approx() - lowerBound.getImag().approx()) * gridDivisions / Math.abs( upperBound.getReal().approx() - lowerBound.getReal().approx() ));
            if (tempZ.getReal().approx() >= 0 && tempZ.getImag().approx() >= 0 && tempZ.getReal().approx() < gridDivisions && tempZ.getImag().approx() < gridDivisions) {
                int x = (int)Math.floor(tempZ.getReal().approx());
                int y = (int)Math.floor(tempZ.getImag().approx());
                densityCount[x][y]++;
                if(densityCount[x][y] > max) max = densityCount[x][y];
            }
        }
    }

    /**
     * Lets externals to read the density matrix created by densityCount().
     * @return the density matrix
     */
    public int[][] getDensityPlot() {
        return densityCount;
    }

    public int getMax() {
        return max;
    }
}
